// models/user_article.go
// 定义具有自定义中间表的多对多关联关系数据模型
// 该文件展示了如何在GORM中自定义多对多关系的中间表结构和配置
// 通过自定义中间表，可以在关联关系中添加额外的字段和元数据
// 相比GORM自动生成的中间表，自定义中间表提供了更大的灵活性和可扩展性
// 本文件还演示了如何使用GORM的生命周期钩子函数来处理中间表记录创建时的额外逻辑
package models

import (
	"fmt"         // 用于打印调试信息
	"time"        // 用于定义时间类型字段
	"gorm.io/gorm" // GORM核心包，提供ORM功能
)

// User1Model 用户模型（多对多关系中的一方）
// 该模型映射到数据库中的user1_models表（GORM默认表名规则：结构体名转为小写复数形式）
// 与Article1Model之间通过自定义中间表实现多对多关联
// 一个用户可以拥有多篇文章，一篇文章可以被多个用户关联
type User1Model struct {
	ID              int64           `gorm:"primaryKey"`       // 主键ID，自动递增，唯一标识用户记录
	Name            string          `gorm:"size:32;not null"` // 用户名，最大长度32字符，不能为空，用于存储用户的名称
	Col1ArticleList []Article1Model `gorm:"many2many:user2_article_models;joinForeignKey:UserID;JoinReferences:ArticleID"`
	// many2many:user2_article_models 指定使用自定义中间表user2_article_models，而不是GORM自动生成的中间表
	// joinForeignKey:UserID 指定当前模型（User1Model）在中间表中的外键字段名为UserID
	// JoinReferences:ArticleID 指定关联模型（Article1Model）在中间表中的外键字段名为ArticleID
	// Col1ArticleList 字段用于在查询用户时预加载其关联的所有文章
}

// Article1Model 文章模型（多对多关系中的另一方）
// 该模型映射到数据库中的article1_models表（GORM默认表名规则：结构体名转为小写复数形式）
// 与User1Model之间通过自定义中间表实现多对多关联
type Article1Model struct {
	ID           int64        `gorm:"primaryKey"`       // 主键ID，自动递增，唯一标识文章记录
	Title        string       `gorm:"size:32;not null"` // 文章标题，最大长度32字符，不能为空，用于存储文章的标题
	CollUserList []User1Model `gorm:"many2many:user2_article_models;joinForeignKey:ArticleID;JoinReferences:UserID"`
	// many2many:user2_article_models 指定使用自定义中间表user2_article_models
	// joinForeignKey:ArticleID 指定当前模型（Article1Model）在中间表中的外键字段名为ArticleID
	// JoinReferences:UserID 指定关联模型（User1Model）在中间表中的外键字段名为UserID
	// CollUserList 字段用于在查询文章时预加载其关联的所有用户
}

// User2ArticleModel 自定义的中间表模型
// 用于存储User1Model和Article1Model之间的多对多关联关系
// 并可以在关联关系中添加额外的字段（如创建时间），这是自定义中间表的主要优势
// 该模型明确映射到数据库中的user2_article_models表
// 通过实现TableName方法确保使用指定的表名
type User2ArticleModel struct {
	UserID       int64         `gorm:"primaryKey"`           // 主键，关联到User1Model的ID字段，作为中间表的一部分主键
	UserModel    User1Model    `gorm:"foreignKey:UserID"`    // 反向关联，指向所属的User1Model，用于从中间表记录直接导航到关联的用户
	ArticleID    int64         `gorm:"primaryKey"`           // 主键，关联到Article1Model的ID字段，作为中间表的另一部分主键
	ArticleModel Article1Model `gorm:"foreignKey:ArticleID"` // 反向关联，指向关联的Article1Model，用于从中间表记录直接导航到关联的文章
	CreatedAt    time.Time     `json:"createdAt"`            // 关联创建时间，额外的元数据字段，记录用户和文章建立关联的时间
	Title        string        `gorm:"size:32" json:"title"` // 文章标题的副本，冗余字段，用于在查询中间表时快速获取文章标题而无需关联查询
	// 双主键设计（UserID和ArticleID）确保每个用户-文章关联是唯一的
	// 这防止了同一个用户和同一篇文章之间建立重复的关联关系
	// 额外的元数据字段（CreatedAt、Title）增强了中间表的功能，使其不仅用于关联，还能存储关联关系的上下文信息
}

// BeforeCreate GORM生命周期钩子函数，在创建User2ArticleModel记录之前执行
// 用于在创建中间表记录时，自动从关联的Article1Model中获取并填充Title字段
// tx: GORM事务对象，用于执行数据库操作
// 返回error: 如果返回非nil的error，GORM将取消当前的创建操作
func (u *User2ArticleModel) BeforeCreate(tx *gorm.DB) error {
	var articleTitle string
	// 根据ArticleID从Article1Model中查询对应的标题
	tx.Model(Article1Model{}).Where("id = ?", u.ArticleID).Select("title").Scan(&articleTitle)
	// 将查询到的标题赋值给当前中间表记录的Title字段
	u.Title = articleTitle
	// 打印调试信息，显示当前处理的ArticleID和获取的标题
	fmt.Println("创建的钩子函数", u.ArticleID, articleTitle)
	// 返回nil表示钩子函数执行成功，允许继续创建操作
	return nil
}

// TableName 自定义表名方法
// 实现GORM的Tabler接口，用于显式指定结构体对应的数据库表名
// 显式指定User2ArticleModel对应的数据库表名为user2_article_models
// 这确保了即使结构体名称和表名不完全遵循GORM的命名约定，也能正确映射
// 在多对多关联中，确保中间表名称的一致性至关重要
func (User2ArticleModel) TableName() string {
	return "user2_article_models" // 返回固定的表名，不受GORM默认命名规则影响
}
